﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using Views;
using System.Diagnostics;

namespace Presenters
{
    /// <summary>
    /// Presenter for the Super Query MainWindow
    /// </summary>
    public class MainWindowPresenter
        : PresenterBase<IMainWindowView>
    {
        /// <summary>
        /// Default name that new unsaved database lists will have
        /// </summary>
        public const string DEFAULT_DB_LIST_NAME = "Untitled.dbl";

        /// <summary>
        /// Default name that new unsaved queries will have
        /// </summary>
        public const string DEFAULT_QUERY_NAME = "Untitled.sql";

        /// <summary>
        /// Initialise the MainWindow presenter
        /// </summary>
        /// <param name="view">An instance that impliments IMainWindowView </param>
        public MainWindowPresenter(IMainWindowView view)
            : base(view)
        {
            // Initialise the view
            View.Queries = new List<IQueryView>();

            // Load up user configuration data
            Services.UserConfigurationService userConfigService = new Services.UserConfigurationService();
            string errorMsg;
            if (!userConfigService.LoadUserConfiguration(out errorMsg))
            {
                // We failed to load user settings so display an error message to the user
                string errorDialogText = string.Format("Unable to load user specific settings due to the following reason:\n\r\n\r{0}", errorMsg);
                Views.ViewManager.ViewFactory.DisplayError(errorDialogText, "User Config Error");
            }

            // Connect the user configuration to this view so we can use it when the user updates the settings via the "Settings"
            // menu
            View.UserConfig = Services.UserConfigurationService.UserConfig;
            
        }

        /// <summary>
        /// Prompt the user for a query name and then open it in a new tab
        /// </summary>
        public void OpenQuery()
        {
            // Get the name of the query to open
            string queryName = ViewManager.ViewFactory.GetFileToOpen("Open Query File", ".sql",
                                                  "SQL Query Files (*.sql;*.qry)|*.sql;*.qry|All Files (*.*)|*.*", View);

            if (!string.IsNullOrEmpty(queryName))
            {
                Services.QueryService queryService = new Services.QueryService();
                string errorMsg;

                // Attempt to load the supplied query
                Models.Query loadedQuery = queryService.LoadQuery(queryName, out errorMsg);

                if (!string.IsNullOrEmpty(errorMsg))
                {
                    // An error occured so display it to the user
                    ViewManager.ViewFactory.DisplayError(string.Format("The following error occured when opening \"{0}\"\n\n{1}",
                                                    queryName, errorMsg), "Error Opening Query");
                }
                else if (loadedQuery != null)
                {
                    // If we managed to open a query then create a new tab and load the query text into it
                    IQueryView newQueryTab = View.NewQueryTab();
                    if (newQueryTab != null)
                    {
                        View.Queries.Add(newQueryTab);
                        newQueryTab.LoadedQuery = loadedQuery;
                        newQueryTab.QueryName = queryName;
                        View.SelectedQueryTab = newQueryTab;
                        newQueryTab.IsDirty = false;
                    }
                }
            }
        }

        /// <summary>
        /// Save the query stored in the supplied IQueryView based view
        /// </summary>
        /// <param name="query">The IQueryView based view to save</param>
        /// <param name="alwaysAskForName">If true then the user is always asked for a new name, even if the query is already saved under a different name</param>
        /// <returns>True if the save went ahead and was successful, false if the save failed and NULL if the save was cancelled</returns>
        public bool? SaveQuery(IQueryView query, bool alwaysAskForName)
        {
            // Assume save is cancelled
            bool? result = null;

            if (query != null)
            {
                // Get the name of the query to save
                string queryName;
                if ((query.QueryName == DEFAULT_QUERY_NAME) || (alwaysAskForName))
                {
                    // No name already exists or we are forced to ask for a new name
                    queryName = ViewManager.ViewFactory.GetFileToSave("Save Query File", ".sql",
                                                          "SQL Query Files (*.sql;*.qry)|*.sql;*.qry|All Files (*.*)|*.*",
                                                          query.QueryName, View);
                }
                else
                {
                    // We already have a name so use it
                    queryName = query.QueryName;
                }

                if (!string.IsNullOrEmpty(queryName))
                {
                    // Attempt to save the query
                    Services.QueryService queryService = new Services.QueryService();
                    string errorMsg;
                    queryService.SaveQuery(queryName, query.LoadedQuery, out errorMsg);

                    if (!string.IsNullOrEmpty(errorMsg))
                    {
                        // An error occured so show the user
                        ViewManager.ViewFactory.DisplayError(string.Format("The following error occured when saving \"{0}\"\n\n{1}",
                                                    queryName, errorMsg), "Error Saving Query");
                        result = false;
                    }
                    else
                    {
                        // No error message so assume the save was successful
                        query.QueryName = queryName;
                        query.IsDirty = false;
                        result = true;
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// Open an existing databas list
        /// </summary>
        /// <param name="query">The query to load the database list in to</param>
        public void OpenDatabaseList(IQueryView query)
        {
            // Get the name of the database list to open
            string listName = ViewManager.ViewFactory.GetFileToOpen("Open Database List File", ".dbl",
                                                  "Database List Files (*.dbl)|*.dbl|All Files (*.*)|*.*", View);

            if (!string.IsNullOrEmpty(listName))
            {
                Services.SavedDatabasesService dbListService = new Services.SavedDatabasesService();
                string errorMsg;

                // Attempt to load the supplied database list
                List<Models.Database> loadedDbList = dbListService.LoadList(listName, out errorMsg);

                // If any databases in the list do not have server ids then they were created with a previous version
                // so upgrade the list to make it work with this version of SuperQuery
                UpgradeDBList(loadedDbList);

                if (!string.IsNullOrEmpty(errorMsg))
                {
                    // An error occured so display it to the user
                    ViewManager.ViewFactory.DisplayError(string.Format("The following error occured when opening \"{0}\"\n\n{1}",
                                                    listName, errorMsg), "Error Opening Database List");
                }
                else if (loadedDbList != null)
                {
                    query.DatabaseList = loadedDbList;
                    query.DisplayedDatabases = loadedDbList;    // By default all loaded databases are displayed
                    query.DatabaseListName = listName;
                    query.IsListDirty = false;

                    // Reset database list filter
                    ResetDBListFilter(query);
                }
            }
        }

        /// <summary>
        /// Save the currently loaded list of databases
        /// </summary>
        /// <param name="query">The query that contains the database list to save</param>
        /// <param name="alwaysAskForName">If true then the user is always asked for a name to save the list under, even if one already exists</param>
        public void SaveDatabaseList(IQueryView query, bool alwaysAskForName)
        {
            // Get the name of the database list to save
            string listName;
            if ((query.DatabaseListName == DEFAULT_DB_LIST_NAME) || (alwaysAskForName))
            {
                // No name already exists or we are forced to ask for a new name
                listName = ViewManager.ViewFactory.GetFileToSave("Save Database List File", ".dbl",
                                                      "Database List Files (*.dbl)|*.dbl|All Files (*.*)|*.*",
                                                      query.DatabaseListName, View);
            }
            else
            {
                // We already have a name so use it
                listName = query.DatabaseListName;
            }

            if (!string.IsNullOrEmpty(listName))
            {
                // Attempt to save the databae list
                Services.SavedDatabasesService savedDatabases = new Services.SavedDatabasesService();
                string errorMsg;
                savedDatabases.SaveList(listName, query.DatabaseList, out errorMsg);

                if (!string.IsNullOrEmpty(errorMsg))
                {
                    // An error occured so show the user
                    ViewManager.ViewFactory.DisplayError(string.Format("The following error occured when saving \"{0}\"\n\n{1}",
                                                listName, errorMsg), "Error Saving Database List");
                }
                else
                {
                    // No error message so assume the save was successful
                    query.DatabaseListName = listName;
                    query.IsListDirty = false;
                }
            }
        }

        /// <summary>
        /// Create a new query
        /// </summary>
        public void NewQuery()
        {
            IQueryView newQueryTab = View.NewQueryTab();

            if (newQueryTab != null)
            {
                // Add the new tab to the collection of open tabs, and select it
                View.Queries.Add(newQueryTab);
                newQueryTab.LoadedQuery = new Models.Query(string.Empty, Models.Query.QueryType.Auto, 30);
                newQueryTab.QueryName = DEFAULT_QUERY_NAME;
                newQueryTab.IsDirty = true;
                View.SelectedQueryTab = newQueryTab;
            }
        }

        /// <summary>
        /// Close a specified query
        /// </summary>
        /// <param name="query">The query to close</param>
        public void CloseQuery(IQueryView query)
        {
            if ((query != null) && (View.Queries.Contains(query)))
            {
                bool closeTab = true;

                // If the query needs to be saved then save it
                if (query.IsDirty)
                {
                    // Ask if the user wants to save this query
                    bool? saveQuery =
                        ViewManager.ViewFactory.AskQuestion(string.Format("\"{0}\" is not saved.\n\nSave now?", query.QueryName),
                                         "Query not saved", true /*Show the cancel button*/);

                    if ((saveQuery.HasValue) && (saveQuery.Value))
                    {
                        SaveQuery(query, false);
                    }
                    else if (!saveQuery.HasValue)
                    {
                        // Cancel was selected so we will not be closing this tab
                        closeTab = false;
                    }
                }

                if (closeTab)
                {
                    // We have a query and it exists in our list of loaded queries so close it down
                    View.CloseQueryTab(query);

                    // Remove the query from the queries collection
                    View.Queries.Remove(query);

                    // We should always have at least one tab open so if the last one is closed, create a new query tab by default
                    if (View.Queries.Count <= 0)
                    {
                        NewQuery();
                    }
                }
            }
        }

        /// <summary>
        /// Create a new database list
        /// </summary>
        /// <param name="query">The query to create a new list for</param>
        public void NewDatabaseList(IQueryView query)
        {
            query.DatabaseList = new List<Models.Database>();
            query.DisplayedDatabases = query.DatabaseList;
            query.DatabaseListName = DEFAULT_DB_LIST_NAME;
            query.IsListDirty = true;

            // Reset database list filter
            ResetDBListFilter(query);
        }


        /// <summary>
        /// Load and display the HTML help in what ever web browser the user has set as their default
        /// </summary>
        public void ShowHelp()
        {
            // Get the path to the help from the application config file
            string helpPath = ConfigurationManager.AppSettings["Help"];

            // If we have a path then load the help file
            if (!string.IsNullOrEmpty(helpPath))
            {
                try
                {
                    Process.Start(helpPath);
                }
                catch (Exception ex)
                {
                    ViewManager.ViewFactory.DisplayError(string.Format("Unable to load help due to the following error:\n\n{0}", ex.Message),
                        "SuperQuery Help Error");
                }
            }
            else
            {
                // No help location has been specified
                ViewManager.ViewFactory.DisplayError("No help location has been configured", "SuperQuery Help Error");
            }

        }

        /// <summary>
        /// Close the application
        /// </summary>
        /// <returns>False is the close process could not proceed</returns>
        public bool CloseApplication()
        {
            // By default assume the application will close
            bool canCloseApplication = true;

            if (View.Queries.Count(q => q.EditorEnabled == false) > 0)
            {
                // If any queries are running queries then prevent the application from being shut down
                // We can work this out by simply seeing if any queries are not in edit mode. If they are not in edit mode
                // then they must be in Run mode
                ViewManager.ViewFactory.DisplayError("Cannot close the application while queries are running. Make sure all queries are cancelled and try again", "Close SuperQuery");
                canCloseApplication = false;
            }
            else
            {
                // Get a list of queries that need saving
                List<IQueryView> queriesToSave = ViewManager.ViewFactory.SaveItems(View.Queries, View);

                if (queriesToSave != null)
                {
                    // Loop through the queries that we want to save and save them
                    foreach (IQueryView query in queriesToSave)
                    {
                        // User wants to save this query
                        bool? saveStatus = SaveQuery(query, false);

                        // If saveStatus is null then we need to cancel the application shut down
                        if (!saveStatus.HasValue)
                        {
                            canCloseApplication = false;
                        }
                    }
                }
                else
                {
                    // The list of queries is null so cancel the application shut down
                    canCloseApplication = false;
                }
            }

            // If we are shutting down the application then save any user configuration settings now
            if (canCloseApplication)
            {
                SaveUserConfiguration();
            }

            return canCloseApplication;
        }

        /// <summary>
        /// Export any canCloseApplication sets from the supplied query tab (allows the user to select export options first)
        /// </summary>
        /// <param name="queryTab">The query tab to export canCloseApplication sets from</param>
        public void ExportResultSets(IQueryView queryTab)
        {
            if (queryTab != null)
            {
                ViewManager.ViewFactory.ExportResults(queryTab.DatabaseList, View);
            }
            else
            {
                // No query tab is selected
                ViewManager.ViewFactory.DisplayError("No Query is currently open so results cannot be exported", "Export Results Error");
            }
        }

        /// <summary>
        /// Show the about dialog box
        /// </summary>
        public void ShowAbout()
        {
            ViewManager.ViewFactory.About(View);
        }

        /// <summary>
        /// Save the current user configuration to a user specific location
        /// </summary>
        public void SaveUserConfiguration()
        {
            Services.UserConfigurationService userConfigService = new Services.UserConfigurationService();
            string errorMsg;
            if (!userConfigService.SaveUserConfiguration(out errorMsg))
            {
                // Something has gone wrong so display an error message
                string errorDialogText = string.Format("Unable to save user configuration due to the following error:\n\r\n\r{0}", errorMsg);
                Views.ViewManager.ViewFactory.DisplayError(errorDialogText, "Save User Confiruation Error");
            }
        }

        /// <summary>
        /// Check to see if any databases in the supplied list need to be upgraded (they have a server id of Guid.Empty)
        /// and upgrade any that do
        /// </summary>
        /// <param name="dbList">The list of databases to check</param>
        private void UpgradeDBList(List<Models.Database> dbList)
        {
            if (dbList != null)
            {
                // Get the number of databases to upgrade
                int dbsToUpgrade = dbList.Count(db => db.DatabaseServer.ServerID == Guid.Empty);

                if (dbsToUpgrade > 0)
                {
                    // We have databases in the list that have no server id so we must ask the user to select one
                    ViewManager.ViewFactory.DBListUpgrade(dbList, View);
                }
            }
        }

        /// <summary>
        /// Reset the filter settings on a specified query tab
        /// </summary>
        /// <param name="queryTab">The query tab on which to reset the filter</param>
        /// <remarks>This does not remove or apply the filter, it simple resets the filter options. It is used when
        /// opening am existing database list of creating a new one as these actions will remove any filter, so we want
        /// the filter settings to match</remarks>
        private void ResetDBListFilter(IQueryView queryTab)
        {
            queryTab.SelectedPluginID = Guid.Empty;
            queryTab.DatabaseNameFilter = string.Empty;
            queryTab.SuccessfullQueryFilter = false;
            queryTab.FilterApplied = false;

        }
    }
}
